package com.txx.myspringboot.listener.weathlistener.eventmulticaster;

import com.txx.myspringboot.listener.weathlistener.event.WeathEvent;
import com.txx.myspringboot.listener.weathlistener.listener.WeatherListener;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public abstract class AbstractWeathEventMulticaster implements WeatherEventMulticaster {

    protected List<WeatherListener<WeathEvent>> listeners = new ArrayList<>();
    private static final Map<Class<?>, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>();

    @Override
    public void addWeathListener(WeatherListener weatherListener) {
        if(!listeners.contains(weatherListener)){
            listeners.add(weatherListener);
        }
    }

    @Override
    public void removeWeathListener(WeatherListener weatherListener) {
        listeners.remove(weatherListener);
    }

    @Override
    public void removeAllWeathListener() {
        listeners.clear();
    }

    @Override
    public void multicasterEvent(WeathEvent weathEvent) {
        listeners.forEach(v -> {
            if(resolveDefaultEventType(weathEvent).isAssignableFrom(resolveDeclaredEventType(v))){
                doStart();
                v.onWeathEvent(weathEvent);
                doEnd();
            }
        });
    }

    abstract void doStart();

    abstract void doEnd();


    /**
     * 获取当前事件类型
     * @param event
     * @return
     */
    private ResolvableType resolveDefaultEventType(WeathEvent event) {
        return ResolvableType.forInstance(event);
    }

    /**
     * 获取监听器中的泛型实际类型
     * @param listener
     * @return
     */
    @Nullable
    private static ResolvableType resolveDeclaredEventType(WeatherListener<WeathEvent> listener) {
        ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
        if (declaredEventType == null || declaredEventType.isAssignableFrom(WeathEvent.class)) {
            Class<?> targetClass = AopUtils.getTargetClass(listener);
            if (targetClass != listener.getClass()) {
                declaredEventType = resolveDeclaredEventType(targetClass);
            }
        }
        return declaredEventType;
    }


    @Nullable
    static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {
        ResolvableType eventType = eventTypeCache.get(listenerType);
        if (eventType == null) {
            eventType = ResolvableType.forClass(listenerType).as(WeatherListener.class).getGeneric();
            eventTypeCache.put(listenerType, eventType);
        }
        return (eventType != ResolvableType.NONE ? eventType : null);
    }
}
